/*
 * ParserStack.java
 *
 * Created on August 2, 2006, 8:26 PM
 *
 */

package galronnlp.perceptron;

import galronnlp.util.BeamStack;
import galronnlp.util.Scorable;
import galronnlp.util.SortedLinkedList;

/**
 *
 * @author Daniel A. Galron
 */
public class ParserStack extends BeamStack {

    FeatureVector parameters;
    double gamma;
    int maxStackSize = 5;
    SortedLinkedList<Node> hypotheses;
    
    boolean D2 = false;
    // Added by D.N. Mehay (4 March 2007)
    // TEMPORARY FIX TO GET TRAINING GOING WITHOUT HYPOTHESYSING EVERYTHING AT FIRST. (D.N.M.)
    boolean randomNoise = false;
    // >>> END TEMP FIX
    
    /** 
     * Creates a new instance of ParserStack 
     * 
     * @param p The parameter vector
     * @param g gamma - note that the gamma function is defined as g * hypotheses.getHighest()
     * @param hypotheses the list of candidate hypotheses
     * @param DEBUG 
     */
    public ParserStack(FeatureVector p, double g, SortedLinkedList<Node> hypotheses, boolean DEBUG, boolean randNoise) {
        this.parameters = p;
        this.gamma = g;
        this.hypotheses = hypotheses;
        D2 = DEBUG;
        randomNoise = randNoise;
    }
    /** 
     * Creates a new instance of ParserStack 
     * 
     * @param p The parameter vector
     * @param g gamma - note that the gamma function is defined as g * hypotheses.getHighest()
     * @param hypotheses the list of candidate hypotheses
     * @param DEBUG 
     */
    public ParserStack(FeatureVector p, double g, SortedLinkedList<Node> hypotheses, boolean DEBUG) {
        this(p,g,hypotheses,DEBUG,false);
    }
    
    
    /**
     * Creates a new parser stack which is a copy of ps
     *
     * @param ps the ParserSTack to copy
     */
    public ParserStack(ParserStack ps) {
        super(ps);
        this.parameters = ps.parameters;
        this.gamma = ps.gamma;
        if(ps.hypotheses != null)
            this.hypotheses = new SortedLinkedList<Node>(ps.hypotheses);
        this.randomNoise = ps.randomNoise;
    }
    
   
    /**
     * The beam is the score of the highest item in the list of candidate hypotheses
     */
    private double beam() {
        double b = hypotheses.getHighest().score();
        if(b!=0.0)
            return b;
        else 
            return 0.001;
    }
    
    /**
     * The gamma function is the absolute value of gamma times the beam.
     * <br>
     * We still need to figure out exactly how to set gamma.
     */
    private double gamma() {
        if(beam()!=0.0) {         
            return Math.abs(gamma * beam());   
        } else {
            return gamma;
        }
    }
    
    /**
     * Decides whether or not to push <code>o</code> onto the stack.
     * 
     * @param o the object to add to the stack
     * @return true if o should be added
     */
    public boolean toAdd(Scorable o) {
        if(hypotheses != null) {
            double score = o.score();
            // TEMPORARY FIX TO GET TRAINING GOING WITHOUT HYPOTHESYSING EVERYTHING AT FIRST. (D.N.M.)
            if(score == 0.0) {
                if(this.randomNoise) {        
                    if(Math.random()>0.5)
                        score = score + (Math.random()*0.01);
                }
            }
            
            // >>>> END TEMP FIX
            //System.err.println("Gamma = " + gamma);
            // Calculate theta: theta_k = SUM(Phi(h0) * parameters) - gamma()/k^3
            double theta = beam() - (gamma()/Math.pow(hypotheses.rank((Node)o), 3));
            // <<<< DEBUG CODE
            if(D2) {
                System.out.print("[");
                if(hypotheses.size() == 0)
                    System.out.println("]");
                else {
                    System.out.print(hypotheses.getFirst().score());
                    for(int i = 1; i < hypotheses.size(); i++) {
                        System.out.print(", " + hypotheses.get(i).score());
                    }
                    System.out.println("]");
                }
                System.out.println(((Node)o).parameters);
                System.out.println("<BEAM=" + beam() + ", GAMMA=" + gamma() + ", SCORE=" + score + ", THETA=" + theta + ">");
            }
            // >>>> END DEBUG CODE
            return score >= theta;
        } else {
            // If the hypothesis list is null, then always return true. This will be
            // the case when first pushing the initial chains onto the stack.            
            return true;
        }
    }
    
    public String printScores() {
        String ret = "[";
        if(this.size() == 0)
            ret += "]";
        else {
            ret += (Double)stack.getFirst().score();
            for(int i = 1; i < stack.size(); i++) {
                ret += ", " + (Double)stack.get(i).score();
            }
            ret += "]";
        }
        return ret;
    }

}
